import java.util.Map.Entry;
import java.util.*;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;

public class GenRunCmds {
	private static final String OUTPUT_DIR = "outdir";
	private static final String MAIN_CLASSES = "mainclasses";
	private static final String JARS = "jars";
	private static final String JAR_SEARCH_ROOT = "jarsearchroot";
	private static final String LIBS = "libs";
	private static final String LIBS_SEARCH_ROOT = "libssearchroot";
	private static final String OS_STRING = "osstr";

	private static final String PATH_SEPARATOR_WIN = ";";
	private static final String LD_PATH_DEF_PREFIX_WIN = "set path=%path%;";
	private static final String LINUX_OS_STRING = "linux";
	private static final String PATH_SEPARATOR_LIN = ":";
	private static final String LD_PATH_DEF_PREFIX_LIN = "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:";

	private static final String DEBUG = "debug";       
	private static final String RELEASE = "release";

	private Map<String, String[]> argmap = new LinkedHashMap<String, String[]>();

	private String absOutDir;

	private boolean isLin;
	private String sharedLibraryExt;
	private String sharedLibraryPrefix;
	private String cmdExt;
	private String pathSeparator;
	private String defineLDPathPrefix;
	private String shellDescr;

	public GenRunCmds(String[] args) {
		args = split(args);
		initArgMap(args);
		absOutDir = new File(argmap.get(OUTPUT_DIR)[0]).getAbsolutePath();
		isLin = argmap.get(OS_STRING)[0].equals(LINUX_OS_STRING);
		pathSeparator = isLin ? PATH_SEPARATOR_LIN : PATH_SEPARATOR_WIN;
		defineLDPathPrefix = isLin ? LD_PATH_DEF_PREFIX_LIN : LD_PATH_DEF_PREFIX_WIN;
		sharedLibraryExt = isLin ? ".so" : ".dll";
		sharedLibraryPrefix = isLin ? "lib" : "";
		cmdExt = isLin ? "" : ".cmd";
		shellDescr = isLin ? "#!/bin/bash\n" : "";
		fillLibDescrs();
	}

	private void initArgMap(String[] args) {
		argmap.put(LIBS, new String[0]);
		argmap.put(LIBS_SEARCH_ROOT, new String[0]);
		for (Entry<String, String> entry : computeStrArgMap(args).entrySet()) {
			argmap.put(entry.getKey(), entry.getValue().split(", ?"));
		}
	}

	private void fillLibDescrs() {
		int libsCount = argmap.get(LIBS).length;
		for (int i = 0; i < libsCount; i++) {
			argmap.get(LIBS)[i] = sharedLibraryPrefix + argmap.get(LIBS)[i] + sharedLibraryExt;
		}
	}

	private Map<String, String> computeStrArgMap(String[] args) {
		Map<String, String> argmap = new LinkedHashMap<String, String>();
		for (int i = 1; i < args.length - 1; i++) {
			if (args[i].equals("=")) {
				String f = args[i - 1];
				String v = args[i + 1];
				argmap.put(f, v);
			}
		}
		return argmap;
	}

	private String[] split(String[] args) {
		List<String> result = new ArrayList<String>();
		for (String a : args) {
			int eqp = a.indexOf('=');
			if (eqp == -1) {
				result.add(a);
			} else {
				String f = a.substring(0, eqp);
				String v = a.substring(eqp + 1);
				result.add(f);
				result.add("=");
				result.add(v);
			}
		}
		return result.toArray(new String[result.size()]);
	}

	public static void main(String[] args) throws IOException {
		new GenRunCmds(args).generate();
	}

	private void generate() throws IOException {
		File outputDirFile = new File(absOutDir);
		if (!outputDirFile.exists()) {
			outputDirFile.mkdirs();
		}
		assert outputDirFile.isDirectory() : "expected directory";
		String classPathStr = computeCP();
		for (String mainClassName : argmap.get(MAIN_CLASSES)) {
			int dtsp = mainClassName.indexOf(':');
			String theClass = mainClassName.substring(0, dtsp);
			int orp = theClass.indexOf('|');
			String args = "";
			if (orp > -1) {
				args = theClass.substring(orp + 1);
				theClass = theClass.substring(0, orp);
			}
			String cmd = mainClassName.substring(dtsp + 1);
			dtsp = cmd.indexOf(':');
			String dbrl = DEBUG;
			if (dtsp > -1) {
				dbrl = cmd.substring(dtsp+1, cmd.length());
				cmd = cmd.substring(0, dtsp);
			}
			cmd = absOutDir + File.separator + cmd + cmdExt;
			PrintWriter pw = new PrintWriter(cmd);
			pw.println(computeEnvInitialiserCmd());
			String assertions = dbrl.equals(DEBUG) ? "-ea" : "-da";
			pw.print("java " + assertions + " -cp ");
			pw.print(classPathStr);
			pw.print(" ");
			pw.print(theClass);
			pw.print(" ");
			pw.print(args);
			pw.close();
			chmodExecutable(cmd);
		}
	}

	private void chmodExecutable(String cmd) throws IOException {
		if (isLin) {
			Runtime.getRuntime().exec("chmod a+x " + cmd);
		}
	}

	private String computeEnvInitialiserCmd() {
		Map<String, String> name2location = computeName2location(argmap.get(LIBS), argmap.get(LIBS_SEARCH_ROOT), false);
		StringBuilder sb = new StringBuilder();
		for (Entry<String, String> itementry : name2location.entrySet()) {
			String item = itementry.getKey();
			String loc = itementry.getValue();
			if (loc == null) {
				throw new RuntimeException(item);
			}
			loc = replaceAbsPath(loc);

			sb.append(sb.length() == 0 ? "" : pathSeparator).append(loc);
		}
		return shellDescr + defineLDPathPrefix + sb.toString();
	}

	private String computeCP() {
		return computeWantedFilesLocations(argmap.get(JARS), argmap.get(JAR_SEARCH_ROOT), pathSeparator);
	}

	private String computeWantedFilesLocations(String[] wantedFiles, String[] searchDirs, String separator) {
		Map<String, String> name2location = computeName2location(wantedFiles, searchDirs, true);
		StringBuilder sb = new StringBuilder();
		for (Entry<String, String> itementry : name2location.entrySet()) {
//			String j = itementry.getKey();
			String loc = itementry.getValue();
			if (loc == null) {
				throw new RuntimeException();
			}
			loc = replaceAbsPath(loc);

			sb.append(sb.length() == 0 ? "" : separator).append(loc);
		}
		return sb.toString();
	}

	private Map<String, String> computeName2location(String[] wantedFiles, String[] searchDirs, boolean includeName) {
		Map<String, String> name2location = new HashMap<String, String>();
		for (String wf : wantedFiles) {
			name2location.put(wf, null);
		}
		for (String searchDir : searchDirs) {
			dfsFiles(searchDir, name2location, includeName);
		}
		return name2location;
	}

	private String replaceAbsPath(String path) {
		path = new File(path).getAbsolutePath();
		int p = 0;
		while (
			p < path.length() &&
			p < absOutDir.length() &&
			path.charAt(p) == absOutDir.charAt(p)
		) {
			p++;
		}
		String remOutDir = absOutDir.substring(p, absOutDir.length());
		while (
			p < path.length() &&
			path.charAt(p) == File.separatorChar
		)
		{
			p++;
		}
		String remPath = path.substring(p, path.length());
		StringBuilder sb = new StringBuilder();
		String resep = File.separator.replace("\\", "\\\\"); // RE
		int moveUpCount = 0;
		for (String pelem : remOutDir.split(resep)) {
			if (pelem.length() > 0) {
				moveUpCount++;
			}
		}
		for (int i = 0; i < moveUpCount; i++) {
			sb.append(sb.length() == 0 ? "" : File.separator).append("..");
		}
		sb.append(sb.length() == 0 ? "" : File.separator).append(remPath);
		return sb.toString();
	}

	private void dfsFiles(String searchRoot, Map<String, String> name2location, boolean includeName) {
		File jsrf = new File(searchRoot);
		if (jsrf.isFile()) {
			String name = jsrf.getName();
			if (name2location.containsKey(name)) {
				String location = includeName ? searchRoot : new File(searchRoot).getParent();
				name2location.put(name, location);
			}
		}
		if (jsrf.isDirectory()) {
			for (String sub : jsrf.list()) {
				dfsFiles(searchRoot + File.separator + sub, name2location, includeName);
			}
		}
	}
}
